package com.google.code.yargon.ui;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Properties;
import java.util.Set;

import org.apache.log4j.Logger;
import org.newdawn.slick.opengl.Texture;
import org.newdawn.slick.opengl.TextureLoader;
import org.newdawn.slick.util.ResourceLoader;

import com.google.code.yargon.util.LogUtil;
import com.google.code.yargon.util.PropertyUtil;

public class TileSet {
	
	// TileSet names
	public static final String TILESET_WARPED = "warped";
	public static final String TILESET_SHADOW = "shadow";
	
	// Property strings that define characters to present level data. Each begins with yargon.tileset.<...>
	public static final String TILE_WALL_H 						= "wall.h";
	public static final String TILE_WALL_V 						= "wall.v";
	public static final String TILE_WALL_C_NE 					= "wall.c.ne";
	public static final String TILE_WALL_C_SE 					= "wall.c.se";
	public static final String TILE_WALL_C_SW 					= "wall.c.sw";
	public static final String TILE_WALL_C_NW 					= "wall.c.nw";
	public static final String TILE_WALL_T_N 					= "wall.t.n";
	public static final String TILE_WALL_T_E 					= "wall.t.e";
	public static final String TILE_WALL_T_S 					= "wall.t.s";
	public static final String TILE_WALL_T_W				 	= "wall.t.w";
	public static final String TILE_WALL_X 						= "wall.x";
	public static final String TILE_DOOR_V_OPEN					= "door.v.open";
	public static final String TILE_DOOR_V_CLOSED				= "door.v.closed";
	public static final String TILE_DOOR_V_BROKEN				= "door.v.broken";
	public static final String TILE_DOOR_H_OPEN					= "door.h.open";
	public static final String TILE_DOOR_H_CLOSED				= "door.h.closed";
	public static final String TILE_DOOR_H_BROKEN				= "door.h.broken";
	public static final String TILE_TUNNEL_H					= "tunnel.h";
	public static final String TILE_TUNNEL_V					= "tunnel.v";
	public static final String TILE_TUNNEL_C_NE					= "tunnel.c.ne";
	public static final String TILE_TUNNEL_C_SE					= "tunnel.c.se";
	public static final String TILE_TUNNEL_C_SW					= "tunnel.c.sw";
	public static final String TILE_TUNNEL_C_NW					= "tunnel.c.nw";
	public static final String TILE_FLOOR_ROOM 					= "floor.room";
	public static final String TILE_FLOOR_TUNNEL				= "floor.tunnel";
	public static final String TILE_CHARACTER 					= "character";
	public static final String TILE_RAT 						= "rat";
	public static final String TILE_UNDEFINED 					= "undefined";
	

	private HashMap<String, Tile> tileSet = null;
	private Texture tileSetTexture;
	
	private String tileSetDir = null;
	private int tileSetSize = 0;
	
	private PropertyUtil props = PropertyUtil.getInstance();
	private Logger log = LogUtil.getInstance();
	
	/** 
	 * Construct a tile set
	 *
	 */
	public TileSet(String setName) {
		readTileSet(setName);
	}
	
	/**
	 * Get a tile by tile name
	 * 
	 * @param tileName tilename as defined in the tiledef file 
	 * @return tile
	 */
	public Tile getTile(String tileName) {
		return tileSet.get(tileName);
	}
	
	/**
	 * Return the tile set texture
	 * @return
	 */
	public Texture getTileTexture() {
		return this.tileSetTexture;
	}
	
	/**
	 * Read the tile set definitions for given tile set. Reads the tile position and determines tile texture coordinates and stores the tile in the tile set
	 * HashMap for later use. Texture coordinates are saved in the Tile object.
	 * 
	 */
	private void readTileSet(String setName) {
		tileSet = new HashMap<String, Tile>();
		
		tileSetDir = props.getString("yargon.tileset."+setName);
		tileSetSize = props.getInt("yargon.tileset."+setName+".size");
		log.debug("readTileSet(): Reading "+tileSetSize+"x"+tileSetSize+" tileset from '"+tileSetDir);
		
		// Read the tiledef file
		Properties tileDef = new Properties();
		FileInputStream fis;
		try {
			fis = new FileInputStream(tileSetDir + "/tiledef");
			tileDef.load(fis); 
		} catch (Exception e) {
			log.error("readTileSet(): Tileset appears invalid, tiledef file was not found: "+e.getMessage());
		}
					
		// Loop through all definitions and calculate texture coordinates according to tile position
		Set<String> tiles = tileDef.stringPropertyNames();
		for(String tileName : tiles) {
			try {
				// Determine tile row and column in the map from its number
				int tileNumber = Integer.parseInt(tileDef.getProperty(tileName));	
				int tr = tileNumber / tileSetSize;
				int tc = tileNumber - (tr * tileSetSize);
			
				// Calculate texture coordinates in pixels
				float tx = tc * tileSetSize;
				float ty = tr * tileSetSize;
				
				// And as normalized texture coordinates
				float texX = tx / 256f;
				float texY = ty / 256f;
				float texW = (float)tileSetSize / 256f;
				float texH = (float)tileSetSize / 256f;
				
				Tile t = new Tile(tileName, texX, texY, texW, texH);
				tileSet.put(tileName, t);		
				log.trace("readTileSet(): Tile created: "+t.toString()+" ("+tx+","+ty+")");
			
			} catch(Exception e) {
				log.error("readTileSet(): Could not determine texture coordinates for tile "+tileName+": "+e.getMessage());
			}
		}
		log.debug("readTileSet(): "+tileSet.size()+" tile definitions read");
		
		// Load the tile set texture map
		try {
			tileSetTexture = TextureLoader.getTexture("PNG", ResourceLoader.getResourceAsStream(tileSetDir +"/"+setName + ".png"), true);
		} catch (IOException e) {
			log.error("readTileSet(): Could not load tileset texture: "+e.getMessage());
		}
	}
	
	/**
	 * Return the tile set size
	 * 
	 * @return
	 */
	public int getSize() {
		return tileSetSize;
	}
}
